#include "animation.h"
#include <fstream>
#include "hge.h"
#include "zlib.h"
#include "util.h"

extern HGE *hge;

Animation::Animation()
{
	mCurframe = -1;
	mPassed = 0;
	mPlaytype = LOOP;
	mInterpolated = 0;
}

Animation::Animation( const Animation &anim )
{
	mCurframe = -1;
	mPassed = 0;
	mPlaytype = anim.mPlaytype;
	mInterpolated = anim.mInterpolated;
	mName = anim.mName;
	mFrames = anim.mFrames;
}

Animation::~Animation()
{

}

void Animation::Update( int diff )
{
	if( mCurframe < 0 || mCurframe >= mFrames.size() )
		return;
	Frame &f = mFrames[mCurframe];
	mPassed += diff;
	if( mPassed >= f.frametime )
	{
		mCurframe++;
		mPassed = 0;
	}
	if(mCurframe >= mFrames.size())
	{
		if( mPlaytype == ONCE )
			mCurframe = -1;
		else if( mPlaytype == LOOP )
			mCurframe = 0;
	}
}

void Animation::Render( int x, int y, int layer, int direction, int w, int h )
{
	if( mCurframe < 0 || mCurframe >= mFrames.size() )
		return;
	Frame &f = mFrames[mCurframe];
	int foot = y + f.sprite->GetHeight();
	float footdepth = float(foot)/10000.0f;
	if( footdepth >= 0.09f )
		footdepth = 0.09f;
	float z = 1.0f - layer*0.1f - footdepth;
	Truncate(z, 0.0f, 1.0f);
	f.sprite->SetZ( z );
	bool xflip = (direction == Direction_LEFT);
	f.sprite->SetFlip( xflip, false );
	if( w==-1 && h==-1 )
		f.sprite->Render(x, y);
	else
		f.sprite->RenderStretch(x,y,x+w,y+h);
}

void Animation::Start()
{
	mCurframe = 0;
	mPassed = 0;
}

Frame * Animation::GetCurFrame()
{
	if( mCurframe == -1 || mCurframe >= mFrames.size() )
		return 0;
	return &mFrames[mCurframe];
}

bool Animation::IsEnd()
{
	if( mCurframe == -1 )
		return true;
	return false;
}

void AnimSet::SetDirection( int direction )
{
	mDirection = direction;
}

bool AnimSet::Load( const char *name )
{
	std::ifstream animfile,texfile;
	const char *path = GeneratePath("animation", name);
	animfile.open(path, std::ios_base::in | std::ios_base::binary );
	if( !animfile.is_open() )
		return false;
	char texfilepath[256] = {0};
	strcpy( texfilepath, path );
	char *append = strrchr( texfilepath, '.' );
	if( append == 0 )
		return false;
	strcpy(append, ".tex");
	texfile.open(texfilepath, std::ios_base::in | std::ios_base::binary );
	if( !texfile.is_open() )
		return false;

	texfile.seekg(0,2);
	uLongf compresssize = texfile.tellg();
	texfile.seekg(0);
	char *compressdata = new char[compresssize];
	texfile.read( compressdata, compresssize );
	texfile.close();
	uLongf texdatasize = *(uLongf *)compressdata;
	compressdata += 4;
	char *texdata = new char[texdatasize];
	char *texdatabegin = texdata;
	uncompress((Bytef*)texdata,&texdatasize, (Bytef *)compressdata,compresssize  );
	compressdata -= 4;

	int stringlen = 0;
	char animsetname[256];
	animfile.read( (char*)&stringlen, sizeof(int) );
	animfile.read( animsetname, stringlen );
	animsetname[stringlen] = 0;
	int animcount = 0;
	animfile.read( (char*)&animcount, sizeof(int) );
	for( int i=0;i<animcount;i++ )
	{
		Animation *a = new Animation();
		animfile.read( (char*)&a->mPlaytype, sizeof(int) );
		animfile.read( (char*)&a->mInterpolated, sizeof(char) );
		char animname[256]={0};
		animfile.read( (char*)&stringlen, sizeof(int) );
		animfile.read( animname, stringlen );
		animname[stringlen] = 0;
		a->mName = animname;
		int framecount = 0;
		animfile.read( (char*)&framecount, sizeof(int) );
		for( int j=0;j<framecount;j++ )
		{
			FrameData f;
			Frame frame;
			animfile.read( (char*)&f, sizeof(FrameData) );
			frame = f;
			int texlen = *(int *)texdata;
			if( texlen == 0 )
			{
				a->mFrames.push_back(frame);
				continue;
			}
			texdata += 4;
			char *texbuf = texdata;
			texdata += texlen;
			HTEXTURE texid = hge->Texture_Load( texbuf, texlen );
			if( texid == 0 )
			{
				a->mFrames.push_back(frame);
				continue;
			}
			float width = hge->Texture_GetWidth( texid );
			float height = hge->Texture_GetHeight( texid );
			frame.sprite = new hgeSprite( texid, 0, 0, width, height );
			a->mFrames.push_back(frame);
		}
		mAnims[a->mName] = a;
	}
	delete []texdatabegin;
	delete []compressdata;
	return true;
}

AnimSet::AnimSet()
{
	mCurAnim = 0;
}

AnimSet::AnimSet( const AnimSet &animset )
{
	mCurAnim = 0;
	for( AnimMap::const_iterator iter = animset.mAnims.begin(); iter != animset.mAnims.end(); iter++ )
	{
		Animation *a = new Animation(*(iter->second));
		mAnims[iter->first] = a;
	}
}

AnimSet::~AnimSet()
{
	for( AnimMap::iterator iter = mAnims.begin(); iter != mAnims.end(); iter++ )
	{
		delete iter->second;
	}
}

void AnimSet::Update( int diff )
{
	if( mCurAnim )
	{
		mCurAnim->Update(diff);
	}
}

void AnimSet::Render( int x, int y , int layer)
{
	if( mCurAnim )
	{
		mCurAnim->Render( x, y, mDirection, layer );
	}
}

void AnimSet::Render( int x, int y,int w, int h, int layer )
{
	if( mCurAnim )
	{
		mCurAnim->Render( x, y, layer,mDirection ,w,h);
	}
}

bool AnimSet::Play( std::string name )
{
	AnimMap::iterator iter = mAnims.find(name);
	if( iter != mAnims.end() )
	{
		mCurAnim = iter->second;
		mCurAnim->Start();
		return true;
	}
	return false;
}

gcn::Rectangle AnimSet::GetCollisionBox()
{
	if( !mCurAnim )
		return gcn::Rectangle(0,0,0,0);
	Frame *f = mCurAnim->GetCurFrame();
	if( f == 0 )
		return gcn::Rectangle(0,0,0,0);
	gcn::Rectangle r = f->collisionbox[0]; 
	AdjustBoxDirection( r );
	return r;
}

gcn::Rectangle AnimSet::GetBoundBox()
{
	if( !mCurAnim )
		return gcn::Rectangle(0,0,0,0);
	Frame *f = mCurAnim->GetCurFrame();
	if( f == 0 )
		return gcn::Rectangle(0,0,0,0);
	hgeSprite *s = f->sprite;
	gcn::Rectangle r = gcn::Rectangle( 0, 0, s->GetWidth(), s->GetHeight() );
	return r;
}

gcn::Rectangle AnimSet::GetWeaponBox()
{
	if( !mCurAnim )
		return gcn::Rectangle(0,0,0,0);
	Frame *f = mCurAnim->GetCurFrame();
	if( f == 0 )
		return gcn::Rectangle(0,0,0,0);
	gcn::Rectangle r = f->weaponbox[0];
	AdjustBoxDirection( r );
	return r;
}

void AnimSet::AdjustBoxDirection( gcn::Rectangle &r )
{
	if( mDirection == Direction_LEFT )
	{
		gcn::Rectangle parent = GetBoundBox();
		r.x = parent.width - (r.x + r.width);
	}
}

AnimManager::AnimManager()
{

}

AnimManager::~AnimManager()
{
	for( AnimSetMap::iterator iter = mAnimSets.begin(); iter != mAnimSets.end(); iter++ )
	{
		AnimSet *animset = iter->second;
		delete animset;
	}
}

AnimSet * AnimManager::Load( const char *name )
{
	AnimSetMap::iterator iter = mAnimSets.find(name);
	if( iter != mAnimSets.end() )
	{
		AnimSet *loaded = iter->second;
		AnimSet *ret = new AnimSet( *loaded );
		return ret;
	}
	else
	{
		AnimSet *newload = new AnimSet;
		if( newload->Load(name) )
		{
			AnimSet *ret = new AnimSet( *newload );
			mAnimSets[name] = newload;
			return ret;
		}
		else
		{
			return 0;
		}
	}
}
